import { HttpEventType, HttpHeaders, HttpParams, HttpUrlEncodingCodec } from '@angular/common/http'
import {
  AdHttpInterceptor,
  AdRequestObservableParams,
  AdRequestPromiseParams,
} from './http.interface'
import { AdHttpService } from './http.service'

export class UrlParameterEncodingCodec extends HttpUrlEncodingCodec {
  encodeKey(key: string): string {
    return encodeURIComponent(key)
  }
  encodeValue(value: string): string {
    return encodeURIComponent(value)
  }
}

export const encoder = new UrlParameterEncodingCodec()

export class AdHttpInterceptorImpl implements AdHttpInterceptor {
  private errorMessage: string = '请求失败，请重试！'
  private httpServiceInstance: AdHttpService
  public defaultParameter: Omit<AdRequestPromiseParams, 'url'> = {
    isLoading: true,
    isNotify: false,
    isPromise: true,
    isThrowErrorNotify: true,
    notifyTitle: '提示',
    notifyMessage: '操作成功',
    errorMessage: '',
    method: 'GET',
    data: null,
    observe: 'body',
    responseType: 'json',
    params: new HttpParams(),
    headers: new HttpHeaders(),
    businessExceptionCodeSet: [],
    handleBusinessExceptionCallBack: () => {},
  }
  constructor(httpServiceInstance: AdHttpService) {
    this.httpServiceInstance = httpServiceInstance
  }
  public requestBefore(
    parameter: AdRequestPromiseParams | AdRequestObservableParams
  ): [HttpParams, HttpHeaders] {
    if (parameter.isLoading) {
      this.httpServiceInstance._AdLoadingService.addLoadingState(parameter.url)
    }
    const { params, headers } = parameter
    const paramObj =
      params instanceof HttpParams ? params : new HttpParams({ fromObject: params, encoder })
    const headerObj = headers instanceof HttpHeaders ? headers : new HttpHeaders(headers)
    return [paramObj, headerObj]
  }
  public async responseHandle(
    value,
    parameter: AdRequestPromiseParams | AdRequestObservableParams
  ) {
    const {
      observe,
      responseType,
      isNotify,
      notifyTitle,
      notifyMessage,
      uploadProgress,
      downloadProgress,
    } = parameter
    if (value?.type === HttpEventType.UploadProgress) {
      return uploadProgress?.(value)
    } else if (value?.type === HttpEventType.DownloadProgress) {
      return downloadProgress?.(value)
    }
    const body = observe === 'body' ? value : value.body
    //对各种响应格式转换成对象后进行结果校验
    //各种格式响应体转换为对象的结果
    let conversionResult
    //json
    if (responseType === 'json') {
      conversionResult = body
    }
    //text
    else if (responseType === 'text') {
      try {
        conversionResult = JSON.parse(body)
      } catch (error) {}
    }
    // blob or arraybuffer
    else if (
      //对响应的大小进行判断 如果大于50kb 则认为是正常数据
      (responseType === 'blob' && body.size < 1024 * 50) ||
      (responseType === 'arraybuffer' && body.byteLength < 1024 * 50)
    ) {
      const blob = responseType === 'arraybuffer' ? new Blob([body]) : body
      try {
        const text = await blob.text()
        conversionResult = JSON.parse(text)
      } catch (error) {}
    }
    const result = this.handleException(conversionResult, parameter)
    if (result !== undefined) {
      return result
    }
    if (isNotify) {
      this.httpServiceInstance.notify.success(notifyTitle, notifyMessage)
    }
    if (
      observe === 'body' &&
      responseType === 'json' &&
      this.isSuccessResponseBody(conversionResult)
    ) {
      return value.data
    }
    return value
  }

  public catchError(
    error: any,
    parameter: AdRequestPromiseParams | AdRequestObservableParams
  ): any {
    const { isThrowErrorNotify, errorMessage } = parameter
    console.error(error)
    if (error === null) {
      throw null
    }
    let message = errorMessage || error?.error?.message || error?.message || this.errorMessage
    if (isThrowErrorNotify) {
      this.httpServiceInstance.notify.error('请求失败', message)
    }
    throw error
  }
  public finalize(parameter: AdRequestPromiseParams | AdRequestObservableParams) {
    if (parameter.isLoading) {
      this.httpServiceInstance._AdLoadingService.removeLoadingState(parameter.url)
    }
  }

  /**
   * 异常处理
   * @param data
   * @returns
   */
  private handleException(data, parameter) {
    if (this.isFailResponseBody(data)) {
      if (parameter.businessExceptionCodeSet.includes(data.code)) {
        return parameter.handleBusinessExceptionCallBack(data)
      }
      throw data
    }
  }

  /**
   * 是否是响应体
   * @param data 要判断的数据结构
   */
  private isResponseBody(data) {
    try {
      const { code, data: result, message, requestId, timestamp } = data
      return [result, code, message, requestId, timestamp].every((item) => item !== undefined)
    } catch (error) {
      return false
    }
  }

  /**
   * 是否是成功响应体
   * @param data
   * @returns
   */
  private isSuccessResponseBody(data) {
    return this.isResponseBody(data) && Number(data.code) === 200
  }

  /**
   * 是否是失败响应体
   * @param data
   * @returns
   */
  private isFailResponseBody(data) {
    return this.isResponseBody(data) && Number(data.code) !== 200
  }
}
